Skip to content

Conversation

@lodev09
Copy link

@lodev09 lodev09 commented Jan 10, 2026

Description

Add RNSLifecycleListenerProtocol to allow external view controllers (such as third-party bottom sheets) to receive screen lifecycle events when a screen in the presenting hierarchy is about to disappear.

This is useful for external modal implementations that need to know when the presenting screen is being unmounted (e.g., when navigating back) so they can dismiss themselves appropriately.

Changes

  • Added new RNSLifecycleListenerProtocol in ios/integrations/
  • Modified RNSScreen.mm:
    • Added _controllerBeforeInvalidate ivar to preserve controller reference before invalidation
    • Updated notifyWillDisappear to notify conforming presented view controllers
    • Updated invalidateImpl to store controller before nulling
  • The protocol method screenWillDisappear:isPresenterUnmounting: provides:
    • The screen controller that is disappearing
    • A flag indicating if the presenter (modal) itself is being unmounted

Before & after - visual documentation

Before

before.mov

After

after.mov

Test plan

  1. Present a third-party modal (e.g., TrueSheet) that conforms to RNSLifecycleListenerProtocol
  2. Navigate back (pop the screen) while the modal is open
  3. The modal should receive screenWillDisappear:isPresenterUnmounting: callback
  4. The modal can use this to dismiss itself when the presenting screen is popped

Example conformance:

@interface MyModalController : UIViewController <RNSLifecycleListenerProtocol>
@end

@implementation MyModalController

- (void)screenWillDisappear:(UIViewController *)screen isPresenterUnmounting:(BOOL)isPresenterUnmounting {
  if (!isPresenterUnmounting) {
    // Screen is being popped, dismiss ourselves
    [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
  }
}

@end

Checklist

  • Included code example that can be used to test this change.
  • Updated / created local changelog entries in relevant test files.
  • For visual changes, included screenshots / GIFs / recordings documenting the change.
  • For API changes, updated relevant public types.
  • Ensured that CI passes

- Add RNSLifecycleListenerProtocol for external view controllers to receive screen lifecycle events
- Notify conforming presented view controllers in notifyWillDisappear
- Preserve controller reference before invalidation to ensure notifications work during unmount
- Pass isPresenterUnmounting flag to help external modals handle dismissal correctly
Copy link
Member

@kkafar kkafar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, wouldn't it be sensible to implement mountingTransactionWillMount and observe for mutation that you're potentially interested in?

If that would be too late in case of e.g. native back button dismissal, you can observe the emitted event here via eventDispatcher from react core. ComponentDescriptor has access to event dispatcher and therefore your components can have it through state you receive in updateState:oldState: from RCTComponentProtocol, see similar setup here with image loader.

Another solution to consider is to have a module that accesses Scheduler (haven't verified whether it's possible, should be) and add a listener for an event there.

For similar reasons that I've pointed out here I'd like to avoid adding component-specific integrations for our lib, especially that we plan on sunsetting this implementation.

@lodev09
Copy link
Author

lodev09 commented Jan 12, 2026

Thanks for your suggestions, @kkafar. I'll try it out. My initial issue was that the _controller becomes nil during view unmount and I needed that to determine if the sheet is actually a presented controller of that screen.

But I'll let you know, maybe this is not needed after all 🙏

@kkafar
Copy link
Member

kkafar commented Jan 12, 2026

Hopefully, if you use mountingTransation**Will**Mount, the controller won't be nil yet.

lodev09 added a commit to lodev09/react-native-true-sheet that referenced this pull request Jan 12, 2026
- Pass EventDispatcher from ComponentDescriptor through State to native view
- Listen for topWillDisappear events to detect when presenter screen unmounts
- Capture presenter screen tag and controller at presentation time
- Distinguish navigation pop from modal dismiss by checking nav stack
- Dismiss sheet when its presenter screen is being popped

This replaces RNSLifecycleListenerProtocol per react-native-screens feedback.
See: software-mansion/react-native-screens#3527
@lodev09
Copy link
Author

lodev09 commented Jan 12, 2026

@kkafar it was a bit hacky but the eventDispatcher via updateState works on all scenarios so far! lodev09/react-native-true-sheet#410

I'll do some more test on our app to see if it doesn't break anything, will close this by then.

Thank you!

lodev09 added a commit to lodev09/react-native-true-sheet that referenced this pull request Jan 12, 2026
* feat(ios): detect screen unmount via C++ EventDispatcher

- Pass EventDispatcher from ComponentDescriptor through State to native view
- Listen for topWillDisappear events to detect when presenter screen unmounts
- Capture presenter screen tag and controller at presentation time
- Distinguish navigation pop from modal dismiss by checking nav stack
- Dismiss sheet when its presenter screen is being popped

This replaces RNSLifecycleListenerProtocol per react-native-screens feedback.
See: software-mansion/react-native-screens#3527

* refactor(ios): extract RNScreensEventObserver from TrueSheetView

* refactor(ios): remove RNSLifecycleListenerProtocol dependency

* fix(ios): dismiss sheet when pushing screen in modal nav stack

* refactor(ios): simplify RNScreensEventObserver modal detection

* chore(example): add test screen to root navigator

* docs(changelog): add PR #410 entry

* docs(navigation): remove PR #3527 reference
@lodev09
Copy link
Author

lodev09 commented Jan 12, 2026

Closing. Thanks again for the guidance @kkafar!

@lodev09 lodev09 closed this Jan 12, 2026
@lodev09 lodev09 deleted the feat/lifecycle-listener-protocol branch January 12, 2026 21:41
@kkafar
Copy link
Member

kkafar commented Jan 13, 2026

Sure! Glad you managed to get it working

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants